
package com.idealighter.game.games.sirenniuniu.manager;

import com.alibaba.fastjson.JSONObject;

import com.google.common.eventbus.Subscribe;
import com.google.inject.Inject;
import com.google.inject.Singleton;

import com.idealighter.game.chat.manager.ChatMsgMgr;
import com.idealighter.game.common.CommonMsgMgr;
import com.idealighter.game.core.assertions.HuohuaAssert;
import com.idealighter.game.core.common.Game;
import com.idealighter.game.core.constant.BoolInteger;
import com.idealighter.game.core.constant.Operator;
import com.idealighter.game.core.constant.notice.NoticeType;
import com.idealighter.game.core.error.ErrorCode;
import com.idealighter.game.dblog.service.DbLogService;
import com.idealighter.game.dictionary.dic.GameNoticeDic;
import com.idealighter.game.dictionary.dic.SirenniuniuTimeDic;
import com.idealighter.game.dictionary.domain.SirenniuniuRoomDomain;
import com.idealighter.game.dictionary.domwrapper.GameNoticeDomainWrapper;
import com.idealighter.game.event.manager.EventMgr;
import com.idealighter.game.event.struct.ClearGameDataEvent;
import com.idealighter.game.event.struct.GameClearExitEvent;
import com.idealighter.game.event.struct.PlayerExitEvent;
import com.idealighter.game.event.struct.ShutdownGameEvent;
import com.idealighter.game.event.struct.ShutdownRoomEvent;
import com.idealighter.game.event.struct.StartGameEvent;
import com.idealighter.game.event.struct.StartRoomEvent;
import com.idealighter.game.gamehall.event.EnterRoomEvent;
import com.idealighter.game.gamehall.event.EnterTableEvent;
import com.idealighter.game.gamehall.event.ExitRoomEvent;
import com.idealighter.game.gamehall.event.ExitTableEvent;
import com.idealighter.game.gamehall.event.RoomMemInfoUpdateEvent;
import com.idealighter.game.games.common.AbstractRoom;
import com.idealighter.game.games.common.InstanceState;
import com.idealighter.game.games.common.SeatState;
import com.idealighter.game.games.common.niuniu.NiuNiuCard;
import com.idealighter.game.games.common.niuniu.NiuNiuCardsType;
import com.idealighter.game.games.sirenniuniu.dto.BillInfo;
import com.idealighter.game.games.sirenniuniu.message.ResGameInfoMsg;
import com.idealighter.game.games.sirenniuniu.struct.SiRenNiuNiuRoom;
import com.idealighter.game.games.sirenniuniu.struct.SiRenNiuNiuSeat;
import com.idealighter.game.games.sirenniuniu.struct.SiRenNiuNiuTable;
import com.idealighter.game.log.core.LogReason;
import com.idealighter.game.log.struct.game.Game13WinLoseLog;
import com.idealighter.game.log.struct.game.GameAfeeLog;
import com.idealighter.game.login.manager.LoginMgr;
import com.idealighter.game.message.core.ResMessage;
import com.idealighter.game.notice.manager.NoticeMsgMgr;
import com.idealighter.game.player.manager.PlayerMgr;
import com.idealighter.game.player.service.bo.PlayerBo;
import com.idealighter.game.player.struct.Player;
import com.idealighter.game.schedule.manager.ScheduleMgr;
import com.idealighter.game.server.ExecutorMgr;
import com.idealighter.game.server.core.executor.DisruptorExecutor;
import com.idealighter.utils.check.EmptyUtil;
import com.idealighter.utils.code.RandCodeUtil;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


/**
 * 四人牛牛逻辑处理 .
 * 
 * 
 * @date 2016年1月20日 下午4:45:27
 *
 */
@Singleton
public class SiRenNiuNiuMgr {

  private static final Logger LOG = LoggerFactory.getLogger(SiRenNiuNiuMgr.class);
  // 玩家手上的牌数
  public static final int PLAYER_CARDS = 5;
  // 四人牛牛定时服务器增加的延时，因为与网络延时等原因
  private static final int ADDED_DELAY = 5;

  private final EventMgr eventMgr;
  // 游戏线程executor,可以执行普通任务和定时任务
  private final DisruptorExecutor gameExecutor;
  @Inject
  private SirenniuniuTimeDic timeDic;
  @Inject
  private SiRenNiuNiuControlScript niuniuControl;
  @Inject
  private PlayerMgr playerMgr;
  @Inject
  private NoticeMsgMgr noticeMsgMgr;
  @Inject
  private ChatMsgMgr chatMsgMgr;
  @Inject
  private ScheduleMgr scheduleMgr;
  @Inject
  private GameNoticeDic gameNoticeDic;
  @Inject
  private CommonMsgMgr commonMsgMgr;
  @Inject
  private SiRenNiuNiuMsgMgr msgMgr;
  @Inject
  private SiRenNiuNiuDataMgr dataMgr;
  @Inject
  private LoginMgr loginMgr;

  /**
   * 构造函数.
   * 
   * @param eventMgr 事件管理.
   * @param executorMgr 执行管理.
   */
  @Inject
  public SiRenNiuNiuMgr(EventMgr eventMgr, ExecutorMgr executorMgr) {
    this.eventMgr = eventMgr;
    eventMgr.register(this);
    this.gameExecutor = executorMgr.getGameExecutor(Game.SIREN_NIUNIU.getModuleId());
  }

  /**
   * 订阅玩家退出游戏事件,在gameExecutor中执行,避免线程安全问题 .
   * 
   * @param event .
   */
  @Subscribe
  public void onPlayerExitGame(PlayerExitEvent event) {
    Player player = event.player;
    if (player != null) {
      long playerId = player.getId();
      if (player.curRoom instanceof SiRenNiuNiuRoom) {
        gameExecutor.execute(() -> {
          SiRenNiuNiuSeat seat = dataMgr.getPlayerSeat(playerId);
          if (seat != null) {
            doExitTabl(seat);
          }

          // 玩家是否在牛牛房间中
          if (player.curRoom instanceof SiRenNiuNiuRoom) {
            // 退出房间
            exitRoom(player);
            // 触发退出
            GameClearExitEvent gameClearExitEvent =
                new GameClearExitEvent(player, Game.SIREN_NIUNIU.getDesc());
            eventMgr.post(gameClearExitEvent);
          }
        });
      }
    }
  }

  /**
   * 检验是否房间关闭，如果桌子不在游戏中或者游戏正在休息，顺便把桌子关闭 .
   *
   * @author abin
   * @date 2018年4月28日 下午3:41:20
   * @param table 桌子.
   */
  private boolean checkClose(SiRenNiuNiuTable table) {
    boolean remove = false;
    if (table.getRoom().getInstanceState() == InstanceState.TO_REMOVE) {
      boolean noExit = false;
      if (table.isReadied()) {
        noExit = true;
      }
      if (!noExit) {
        for (Long playerId : table.players()) {
          Player player = playerMgr.getPlayer(playerId);
          if (player != null) {
            kickoutForRoomClose(player);
          }
        }
        SiRenNiuNiuRoom room = table.getRoom();
        room.removeTable(table.getId().intValue());

        table = null;
        remove = true;
      }

    }
    return remove;
  }

  private void deleteRooms(List<SiRenNiuNiuRoom> deleteRooms) {
    if (EmptyUtil.listIsNotEmpty(deleteRooms)) {
      for (Iterator<SiRenNiuNiuRoom> iterator = deleteRooms.iterator(); iterator.hasNext();) {
        SiRenNiuNiuRoom room = iterator.next();
        List<SiRenNiuNiuTable> tables = new ArrayList<>(room.tables());
        for (Iterator<SiRenNiuNiuTable> tableIterator = tables.iterator(); tableIterator
            .hasNext();) {
          SiRenNiuNiuTable table = tableIterator.next();
          checkClose(table);
        }
        dataMgr.checkRemoveInstance(room.getId().intValue(), room.getInstanceId());
      }
    }
  }

  /**
   * 开启房间.
   * 
   * @param event .
   */
  @Subscribe
  public void onStartRoom(StartRoomEvent event) {
    gameExecutor.execute(() -> {
      if (event.getGame() == Game.SIREN_NIUNIU) {
        List<SiRenNiuNiuRoom> deleteRooms = dataMgr.startRoom(event.getRoomId(), true);
        deleteRooms(deleteRooms);
      }
    });
  }

  /**
   * 监听关闭房间.
   * 
   * @param event 关闭房间事件.
   */
  @Subscribe
  public void onShutDownRoom(ShutdownRoomEvent event) {
    gameExecutor.execute(() -> {
      if (event.getGame() == Game.SIREN_NIUNIU) {
        List<SiRenNiuNiuRoom> deleteRooms = dataMgr.deleteRoom(event.getRoomId());
        deleteRooms(deleteRooms);
      }

    });
  }

  /**
   * 开启游戏 .
   *
   * @author abin
   * @date 2018年5月18日 上午11:24:51
   * @param event 开启游戏事件.
   */
  @Subscribe
  public void onStartGame(StartGameEvent event) {
    gameExecutor.execute(() -> {
      if (event.getGame() == Game.SIREN_NIUNIU) {
        List<Integer> ids = dataMgr.reloadRoom();
        if (EmptyUtil.listIsNotEmpty(ids)) {
          for (Integer id : ids) {
            List<SiRenNiuNiuRoom> deleteRooms = dataMgr.startRoom(id, false);
            deleteRooms(deleteRooms);
          }
        }
      }
    });
  }

  /**
   * 关闭游戏事件.
   * 
   * @param event 事件.
   */
  @Subscribe
  public void onShutdownGame(ShutdownGameEvent event) {
    gameExecutor.execute(() -> {
      if (event.getGame() == Game.SIREN_NIUNIU) {
        Collection<SiRenNiuNiuRoom> rooms = dataMgr.allRooms();
        if (!EmptyUtil.isEmpty(rooms)) {
          for (SiRenNiuNiuRoom room : rooms) {
            List<SiRenNiuNiuRoom> deleteRooms = dataMgr.deleteRoom(room.getId().intValue());
            deleteRooms(deleteRooms);
          }
        }
      }
    });
  }

  @Subscribe
  public void onClearGameData(ClearGameDataEvent event) {
    gameExecutor.execute(() -> doClearGameData(event.playerId));
  }

  /**
   * 进入游戏大厅 .
   * 
   * @param player 玩家.
   */
  public ResMessage enterGameHall(Player player) {
    if (player.curRoom != null) {
      Game game = player.curRoom.game();
      LOG.warn("[四人牛牛]玩家[{}][{}]在[{}]游戏中，暂时无法进入游戏", player.getId(), player.getPlayerName(),
          game.getDesc());
      HuohuaAssert.isTrue(false, ErrorCode.GAME_ALREADY_IN, game.getDesc());
    }
    LOG.info("[四人牛牛]玩家[{}][{}]进入大厅成功", player.getPlayerName(), player.getId());
    return msgMgr.sendEnterGameHallMsg();
  }

  /**
   * 玩家进入房间 .
   * 
   * @param player 玩家.
   * @param roomId .
   */
  public ResMessage enterRoom(Player player, int roomId) {
    AbstractRoom curRoom = player.curRoom;
    if (curRoom != null) {
      Game game = curRoom.game();
      LOG.warn("[四人牛牛]玩家[{}][{}]当前在游戏[{}]房间[{}]中不能进入房间", player.getId(), player.getPlayerName(),
          curRoom.getId());
      HuohuaAssert.isTrue(false, ErrorCode.GAME_ALREADY_IN, game.getDesc());
    }

    SiRenNiuNiuRoom room = dataMgr.getNewestRoom(roomId);

    HuohuaAssert.isTrue(room != null && room.getInstanceState().equals(InstanceState.NORMAL),
        ErrorCode.GAME_ROOM_CLOSED);

    SirenniuniuRoomDomain roomDom = room.getRoomDomain();

    if (roomDom.getIsActive() != 1) {
      LOG.error("[四人牛牛]玩家[{}][{}]进入的房间[{}]已经关闭", player.getPlayerName(), player.getId(), roomId);
      HuohuaAssert.isTrue(false, ErrorCode.GAME_ROOM_CLOSED);
    }

    if (player.getGold() < roomDom.getLower()) {
      LOG.warn("[四人牛牛]玩家[{}][{}]进入的房间[{}]低于下限[{}]金币", player.getPlayerName(), player.getId(),
          roomId, roomDom.getLower());
      HuohuaAssert.isTrue(false, ErrorCode.PLAYER_GOLD_NOT_ENOUGH);
    }

    long upper = roomDom.getUpper();
    if (upper > 0L && player.getGold() > upper) {
      LOG.warn("[四人牛牛]玩家[{}][{}]进入的房间[{}]高于上限[{}]金币", player.getPlayerName(), player.getId(),
          roomId, roomDom.getUpper());
      HuohuaAssert.isTrue(false, ErrorCode.PLAYER_GOLD_BEYOND_MAX_GOLD);
    }

    if (!player.vip()
        && room.getPlayers().size() >= roomDom.getMaxNum() * roomDom.getOrdinarPeople() / 100) {
      LOG.warn("[四人牛牛]玩家[{}][{}]进入的房间[{}]普通玩家人数已超过上限", player.getPlayerName(), player.getId(),
          roomId, roomDom.getMaxNum());
      HuohuaAssert.isTrue(false, ErrorCode.GAME_NOT_VIP_ROOM_FULL);
    }

    if (room.getPlayers().size() >= roomDom.getMaxNum()) {
      commonMsgMgr.sendErrorDlgMsg(player, ErrorCode.GAME_ROOM_FULL, Operator.SYSTEM);
      LOG.warn("[四人牛牛]玩家[{}][{}]进入的房间[{}]已超过最大人数[{}]", player.getPlayerName(), player.getId(),
          roomId, roomDom.getMaxNum());
      HuohuaAssert.isTrue(false, ErrorCode.GAME_ROOM_FULL);
    }

    // 触发进入房间事件
    eventMgr.post(new EnterRoomEvent(player, room));

    LOG.info("[四人牛牛]玩家[{}][{}]进入房间[{}]成功", player.getPlayerName(), player.getId(), roomId);
    // 修改玩家当前房间
    player.curRoom = room;
    room.getPlayers().add(player.getId());
    // 发送进入房间消息
    return msgMgr.sendEnterRoomMsg(player);
  }

  /**
   * 获取游戏最新信息 .
   */
  public ResMessage gameLastInfo(Player player) {
    AbstractRoom curRoom = player.curRoom;
    SiRenNiuNiuSeat seat = dataMgr.getPlayerSeat(player.getId());

    ResMessage result = null;
    if (curRoom != null && seat != null) {
      SiRenNiuNiuRoom room = (SiRenNiuNiuRoom) curRoom;
      seat.setTotalChips(convertChip(room.getRoomDomain(), player.getGold()));
      result = msgMgr.sendGameLastInfo(seat);
    } else {
      result = new ResGameInfoMsg();
    }
    return result;
  }

  public ResMessage resExitRoom(Player player) {
    exitRoom(player);
    return ResMessage.DEFAULT;
  }

  private void doExitRoom(Player player) {
    AbstractRoom curRoom = player.curRoom;
    SiRenNiuNiuSeat seat = dataMgr.getPlayerSeat(player.getId());

    HuohuaAssert.isTrue(curRoom instanceof SiRenNiuNiuRoom);
    HuohuaAssert.isTrue(seat == null, ErrorCode.GAME_EXIT_ROOM_EXIT_SIT_FIRST);

    player.curRoom = null;
    SiRenNiuNiuRoom room = (SiRenNiuNiuRoom) curRoom;
    room.getPlayers().remove(player.getId());

    LOG.info("[四人牛牛]玩家[{}][{}]退出房间[{}]", player.getPlayerName(), player.getId(), room.getId());
    // 触发退出房间事件
    eventMgr.post(new ExitRoomEvent(player, curRoom));
  }

  /**
   * 玩家退出房间 .
   * 
   * @param player 玩家.
   */
  public void exitRoom(Player player) {
    doExitRoom(player);

    msgMgr.sendExitRoom(player);
  }

  /**
   * 筹码换算金币,只会是多个金币等于一个筹码 .
   * 
   * @param roomDomain .
   * @param chips .
   */
  public long convertGold(SirenniuniuRoomDomain roomDomain, long chips) {
    return chips * roomDomain.getProportionGold() / roomDomain.getProportionChips();
  }

  /**
   * 金币换算筹码 .
   * 
   * @param roomDomain .
   * @param gold .
   * @return
   */
  public long convertChip(SirenniuniuRoomDomain roomDomain, long gold) {
    return gold * roomDomain.getProportionChips() / roomDomain.getProportionGold();
  }

  /**
   * 快速进入二人牛牛 .
   * 
   * @param player 玩家.
   * @param roomId .
   */
  public ResMessage fastEnterTable(Player player, int roomId) {
    if (!(player.curRoom instanceof SiRenNiuNiuRoom)) {
      LOG.error("[四人牛牛]玩家[{}][{}]当前不在房间中不能直接进入桌子", player.getPlayerName(), player.getId());
      HuohuaAssert.isTrue(false);
    }

    SiRenNiuNiuSeat seat = dataMgr.getPlayerSeat(player.getId());
    if (seat != null) {
      LOG.error("[四人牛牛]玩家[{}][{}]已经在牌桌中不能重复进入牌桌", player.getPlayerName(), player.getId());
      HuohuaAssert.isTrue(false);
    }

    SiRenNiuNiuRoom room = (SiRenNiuNiuRoom) player.curRoom;

    HuohuaAssert.isTrue(room != null && room.getInstanceState().equals(InstanceState.NORMAL),
        ErrorCode.GAME_ROOM_CLOSED);

    SiRenNiuNiuSeat emptySeat = findEmptySeat(player, room);
    HuohuaAssert.isTrue(emptySeat != null, ErrorCode.GAME_ALREADY_IN_MATCH);

    doEnterTable(player, emptySeat, null);
    return ResMessage.DEFAULT;
  }

  /**
   * 玩家进入座位 .
   * 
   * @param player 玩家.
   * @param seat 座位信息.
   */
  private void doEnterTable(Player player, SiRenNiuNiuSeat seat, String tablePwd) {
    int roomId = seat.getTable().getRoom().getId().intValue();
    SiRenNiuNiuTable table = seat.getTable();

    if (table.isReadied()) {
      LOG.warn("[四人牛牛]玩家[{}][{}]进入的桌子[{}]已经开始游戏", player.getPlayerName(), player.getId(),
          table.getId());
      HuohuaAssert.isTrue(false);
    }

    SirenniuniuRoomDomain roomDom = table.getRoom().getRoomDomain();
    if (player.getGold() < roomDom.getLower()) {
      LOG.warn("[四人牛牛]玩家[{}][{}]进入的房间[{}]低于下限[{}]金币", player.getPlayerName(), player.getId(),
          roomId, roomDom.getLower());
      HuohuaAssert.isTrue(false, ErrorCode.PLAYER_GOLD_NOT_ENOUGH);
    }

    long upper = roomDom.getUpper();
    if (upper > 0L && player.getGold() > upper) {
      LOG.warn("[四人牛牛]玩家[{}][{}]进入的房间[{}]高于上限[{}]金币", player.getPlayerName(), player.getId(),
          roomId, roomDom.getUpper());
      HuohuaAssert.isTrue(false, ErrorCode.PLAYER_GOLD_BEYOND_MAX_GOLD);
    }

    int afee = roomDom.getAfee();
    if (afee > 0 && player.getGold() < roomDom.getAfee()) {
      LOG.warn("[四人牛牛]玩家[{}][{}]进入桌子金币不足台费", player.getPlayerName(), player.getId());
      HuohuaAssert.isTrue(false, ErrorCode.GAME_NOT_ENOUGH_FEE);
    } else {
      playerMgr.minusGold(player, roomDom.getAfee(), LogReason.SIREN_NIUNIU_ENTER_TABLE_MINUS_AFEE);
      DbLogService.log(new GameAfeeLog(player, table, roomDom.getAfee()));
    }

    seat.setPlayerId(player.getId());
    seat.setPlayerName(player.getPlayerName());
    seat.setSex(player.getSex());
    seat.setRobot(player.robot());
    seat.setState(SeatState.SEATED);
    seat.setTotalChips(convertChip(roomDom, player.getGold()));

    // 更新玩家座位
    dataMgr.updatePlayerSeats(player.getId(), seat);
    // 触发进入桌子事件
    eventMgr.post(new EnterTableEvent(player, seat));

    player.curSeat = seat;

    // 向玩家发送进入牌桌结果信息
    msgMgr.sendEnterTableMsg(player, table, seat.getTotalChips());
    msgMgr.sendOtherEnterTableMsg(seat);
    LOG.info("[四人牛牛]玩家[{}][{}]进入桌子[{}]座位[{}]成功", player.getPlayerName(), player.getId(),
        table.getId(), seat.getOrder());

    // 强制退出
    int readyTime = timeDic.map().get(1).getTime();
    delayExit(seat, readyTime + ADDED_DELAY, TimeUnit.SECONDS);
  }

  /**
   * 玩家退出牌桌 .
   * 
   * @param player 玩家.
   */
  public ResMessage exitTable(Player player) {
    SiRenNiuNiuSeat seat = dataMgr.getPlayerSeat(player.getId());
    if (seat == null) {
      LOG.error("玩家[{}][{}]没在牌桌中不能退出牌桌", player.getPlayerName(), player.getId());
      HuohuaAssert.isTrue(false);
    }

    // 退出桌子
    doExitTabl(seat);
    return ResMessage.DEFAULT;
  }

  private void kickoutForRoomClose(Player player) {
    SiRenNiuNiuSeat seat = dataMgr.getPlayerSeat(player.getId());
    if (seat != null) {
      doExitTabl(seat);
    }
    if (player.curRoom != null) {
      doExitRoom(player);
      commonMsgMgr.noticeRoomCloseKickout(player);
    }

  }



  /**
   * 退出桌子 . 1.玩家正常退出牌桌,能在在线玩家中拿到player,正常退出桌子 2.玩家非正常退出(如掉线)后,从活跃玩家或数据库中拿到PlayerDomain，结算后马上更新缓冲和数据库
   * 
   * @param seat 座位信息.
   */
  private void doExitTabl(SiRenNiuNiuSeat seat) {
    long playerId = seat.getPlayerId();
    final Player player = playerMgr.getPlayer(playerId);
    SiRenNiuNiuTable table = seat.getTable();

    boolean gameOver = false;
    boolean checkBetOver = false;
    boolean checkPlayerNum = false;
    boolean checkGameOver = false;
    List<BillInfo> payBackInfos = new ArrayList<>();

    /*
     * 退桌处理 . 1.发牌前，任何人退都重新开始 2.发牌后，闲家退闲家摊牌结算，庄家退，都摊牌结算
     */
    if (seat.getCards().size() == 0) { // 发牌前(准备,叫庄,下注)
      if (table.isReadied()) { // (叫庄|下注)
        if (!table.callDealerOver()) { // 叫庄阶段
          seat.setCallDealer(null);
          checkPlayerNum = true;
        } else if (!table.betOver()) { // 下注阶段
          // 如果是庄家退出,退回金币，并游戏结束
          if (seat.isDealer()) {
            // 归还金币，游戏结束
            for (SiRenNiuNiuSeat s : table.getSeats()) {
              if (s.getPlayerId() > 0 && s.getBetedchips() > 0L) {
                // 退回金币
                if (s.getBetedchips() > 0L) {
                  BillInfo payBackInfo = new BillInfo();
                  payBackInfo.setOrder(seat.getOrder());
                  payBackInfo.setChips(seat.getBetedchips());
                  payBackInfos.add(payBackInfo);

                  seat.setTotalChips(seat.getTotalChips() + seat.getBetedchips());
                  Player p = playerMgr.getPlayer(playerId);
                  long billGold =
                      convertGold(s.getTable().getRoom().getRoomDomain(), s.getBetedchips());
                  playerMgr.addGold(p, billGold, LogReason.SIREN_NIUNIU_PAY_BACK);
                  // 数据库输赢日志
                  DbLogService.log(new Game13WinLoseLog(p.playerBo(), s, s.getBetedchips(),
                      billGold, LogReason.SIREN_NIUNIU_PAY_BACK));
                }
              }
            }
            gameOver = true;
          } else { // 闲家退出
            // 归还玩家的金币
            if (seat.getBetedchips() > 0L) {
              // 退回金币
              long billGold =
                  convertGold(seat.getTable().getRoom().getRoomDomain(), seat.getBetedchips());
              playerMgr.addGold(player, billGold, LogReason.SIREN_NIUNIU_PAY_BACK);
              // 数据库输赢日志
              DbLogService.log(new Game13WinLoseLog(player.playerBo(), seat, seat.getBetedchips(),
                  billGold, LogReason.SIREN_NIUNIU_PAY_BACK));
            }
            checkBetOver = true;
          }
          checkPlayerNum = true;
        }
      }
    } else { // 发牌后
      if (!seat.isShowdown()) { // 已经发牌,没有摊牌则摊牌并结算
        doShowdown(seat, true);
      }

      if (seat.getBillChips() != 0) {
        doBill(seat);
      }
      checkGameOver = true;
    }
    // 发送退出房间消息
    msgMgr.sendExitTableMsg(seat);
    dataMgr.removePlayerSeat(seat.getPlayerId());
    LOG.info("[四人牛牛]玩家[{}][{}]退出桌子[{}]", seat.getPlayerId(), seat.getPlayerName(),
        seat.getTable().getId());

    seat.clear();
    player.curSeat = null;
    player.curTable = null;

    // 触发退出桌子事件
    eventMgr.post(new ExitTableEvent(player, seat));

    // 游戏结束
    if (gameOver) {
      for (SiRenNiuNiuSeat s : table.getSeats()) {
        if (s.getPlayerId() > 0) {
          s.resetButTotalChips();
        }
      }
      msgMgr.sendGameOver(table, payBackInfos);
      table.reset();
      return;
    }

    if (checkPlayerNum) { // 如果人数不够，直接退出
      int num = 0;
      for (SiRenNiuNiuSeat s : table.getSeats()) {
        if (s.getPlayerId() > 0) {
          num++;
        }
      }
      // 人数不够，游戏结束
      if (num < 2) {
        for (SiRenNiuNiuSeat s : table.getSeats()) {
          if (s.getPlayerId() > 0) {
            s.resetButTotalChips();
          }
        }
        msgMgr.sendGameOver(table, payBackInfos);
        table.reset();
        return;
      }
    }

    if (checkGameOver) {
      int num = 0;
      for (SiRenNiuNiuSeat s : table.getSeats()) {
        if (s.getPlayerId() > 0) {
          num++;
        }
      }
      // 人数不够，游戏结束
      if (num < 2) {
        gameOver(table);
        return;
      }

    }

    if (!table.isReadied() && readiedOver(table)) { // 3个人在桌子中，2个人准备，剩下那个人没有准备则准备结束
      readyOver(table);
      return;
    }

    if (checkBetOver) { // 校验下注是否完成
      if (table.betOver()) {
        LOG.info("[四人牛牛]闲家[{}][{}]退吹房间,开始洗牌发牌", seat.getPlayerName(), seat.getPlayerId());
        doDealCards(table);
        return;

      }
    }
  }

  /**
   * 玩家准备 .
   * 
   * @param player 玩家.
   */
  public ResMessage ready(Player player) {
    // 座位
    SiRenNiuNiuSeat seat = dataMgr.getPlayerSeat(player.getId());

    if (seat == null) {
      LOG.error("[四人牛牛]玩家[{}][{}]还未进入牌桌不能准备", player.getPlayerName(), player.getId());
      HuohuaAssert.isTrue(false);
    }

    if (seat.readied()) {
      LOG.warn("[四人牛牛]玩家[{}][{}]已经准备好,请勿重复准备", player.getPlayerName(), player.getId());
      return ResMessage.DEFAULT;
    }

    SiRenNiuNiuTable table = seat.getTable();
    SirenniuniuRoomDomain roomDomain = table.getRoom().getRoomDomain();

    // 校验玩家身上的钱换成的筹码是否足够(底数*最小下注倍数*最大赔率*其他桌人数)
    int maxMultiple = NiuNiuCardsType.getMaxMultiple();
    int lefPersonNum = roomDomain.getChair() - 1;
    long minChips =
        roomDomain.getBaseBet() * roomDomain.getMinBetMultiple() * maxMultiple * lefPersonNum;
    if (seat.getTotalChips() < minChips) {
      LOG.warn("[四人牛牛]玩家[{}][{}]玩家身上的筹码不足[{}(底注[{}] * 最低下注倍数[{}] . * 最大赔率[{}] . * 桌子剩余人数[{}])]不能准备",
          player.getPlayerName(), player.getId(), minChips, roomDomain.getBaseBet(),
          roomDomain.getMinBetMultiple(), maxMultiple, lefPersonNum);
      HuohuaAssert.isTrue(false, ErrorCode.GAME_CHIP_NOT_ENOUGH);
    }

    seat.setState(SeatState.READY);
    // 清除准备计时
    if (seat.stepFuture != null) {
      seat.stepFuture.cancel(false);
    }



    LOG.info("[四人牛牛]玩家[{}][{}]准备好", player.getPlayerName(), player.getId());
    msgMgr.sendReadyMsg(seat);

    // 向同桌的玩家发送该座位的玩家已经准备好
    eventMgr.post(new RoomMemInfoUpdateEvent(player));

    /*
     * 计算桌子是否准备好 .
     */
    if (readiedOver(table)) { // 都准备完成,对所有玩家进行游戏过程定时(没有对每个步骤定时)
      readyOver(table);
    }

    return ResMessage.DEFAULT;
  }

  /**
   * 桌子是否准备好,会改变桌子readied值 .
   * 
   * @param table .
   * @return 返回桌子是否准备好
   */
  private boolean readiedOver(SiRenNiuNiuTable table) {
    boolean tableReadied = true;
    // 准备好的玩家
    int readiedNum = 0;
    for (SiRenNiuNiuSeat s : table.getSeats()) {
      if (s.getPlayerId() > 0) {
        if (!s.readied()) {
          tableReadied = false;
          break;
        }
        readiedNum++;
      }
    }
    if (readiedNum <= 1) { // 必须大于1人才算准备好
      tableReadied = false;
    }

    table.setReadied(tableReadied);
    return tableReadied;
  }

  /**
   * 桌子全部准备完成 .
   * 
   * @param table .
   */
  private void readyOver(SiRenNiuNiuTable table) {
    // 都准备完成后对游戏过程(叫庄、下注、摊牌)定时,防止玩家卡在游戏中,时间到了后如果已经发牌自动结算退出
    // int gameTime = timeDic.get(2).getTime() + timeDic.get(3).getTime() + timeDic.get(4).getTime()
    // + timeDic.get(5).getTime();
    int callBankerTime = timeDic.get(2).getTime();
    for (SiRenNiuNiuSeat s : table.getSeats()) {
      if (s.getPlayerId() > 0) {
        s.setState(SeatState.GAMING);
        Player player = playerMgr.getPlayer(s.getPlayerId());
        eventMgr.post(new RoomMemInfoUpdateEvent(player));

        // 强制退出
        delayExit(s, callBankerTime + ADDED_DELAY, TimeUnit.SECONDS);
      }
    }

    msgMgr.sendReadyOverMsg(table);
  }

  private void delayExit(SiRenNiuNiuSeat s, int delay, TimeUnit unit) {
    // 清除准备计时
    if (s.stepFuture != null) {
      s.stepFuture.cancel(false);
    }
    // 叫庄时间内未叫庄,强制退出
    s.stepFuture = scheduleMgr.schedule(() -> {
      Player player = playerMgr.getPlayer(s.getPlayerId());
      doExitTabl(s);
      exitRoom(player);
    }, delay, unit, gameExecutor);
  }

  private void delayShowCard(SiRenNiuNiuSeat s, int delay, TimeUnit unit) {
    // 清除准备计时
    if (s.stepFuture != null) {
      s.stepFuture.cancel(false);
    }
    // 叫庄时间内未叫庄,强制退出
    s.stepFuture = scheduleMgr.schedule(() -> {
      Player player = playerMgr.getPlayer(s.getPlayerId());
      showdown(player, new ArrayList<>());
    }, delay, unit, gameExecutor);
  }


  /**
   * 玩家换桌 .
   * 
   * @param player 玩家.
   */
  public ResMessage exchangeTable(Player player) {
    SiRenNiuNiuSeat seat = dataMgr.getPlayerSeat(player.getId());
    if (seat == null) {
      LOG.error("玩家[{}][{}]没在座位中不能换位置", player.getPlayerName(), player.getId());
      HuohuaAssert.isTrue(false);
    }

    if (seat.readied()) {
      LOG.error("[四人牛牛]玩家[{}][{}]已经准备好不能换桌子", player.getPlayerName(), player.getId());
      HuohuaAssert.isTrue(false, ErrorCode.SIREN_NIUNIU_COULD_NOT_EXCHANGE_TABLE_GAME_READY);
    }

    SiRenNiuNiuRoom room = seat.getTable().getRoom();

    HuohuaAssert.isTrue(player.getGold() >= room.getRoomDomain().getLower(),
        ErrorCode.PLAYER_GOLD_NOT_ENOUGH);

    HuohuaAssert.isTrue(player.getGold() <= room.getRoomDomain().getUpper(),
        ErrorCode.PLAYER_GOLD_BEYOND_MAX_GOLD);

    // 退出桌子
    doExitTabl(seat);
    // 交换到其他桌的位置
    SiRenNiuNiuSeat exchangedSeat = findEmptySeat(player, room);
    if (exchangedSeat == null) {
      LOG.warn("[四人牛牛]当前房间没有空位置给玩家[{}][{}]交换", player.getPlayerName(), player.getId());
      HuohuaAssert.isTrue(false, ErrorCode.SIREN_NIUNIU_COULD_NOT_EXCHANGE_HAVE_NO_OTHER_TABLE);
    } else {
      // 进入位置
      doEnterTable(player, exchangedSeat, null);
    }
    return ResMessage.DEFAULT;
  }

  /**
   * 查找空位置,优先查找有人的桌子 .
   * 
   * @param player 玩家.
   * @param room .
   * @return
   */
  private SiRenNiuNiuSeat findEmptySeat(Player player, SiRenNiuNiuRoom room) {
    SiRenNiuNiuSeat emptySeat = null;
    ArrayList<SiRenNiuNiuTable> tables = new ArrayList<>(room.getTables().values());
    Collections.shuffle(tables);

    for (SiRenNiuNiuTable table : tables) {
      if (table.getPassword() != null) {
        continue;
      }
      if (table.isReadied()) {
        continue;
      }

      if (table.getLimitIp() != null && table.getLimitIp().equals(player.ip)) {
        continue;
      }

      if (player.getGold() < table.getLimitGold()) {
        continue;
      }

      boolean hasPlayer = false;
      SiRenNiuNiuSeat tableEmptySeat = null;

      for (SiRenNiuNiuSeat seat : table.getSeats()) {
        if (seat.getPlayerId() == 0) {
          emptySeat = seat;
          tableEmptySeat = seat;
        } else {
          hasPlayer = true;
        }
      }

      if (hasPlayer && tableEmptySeat != null) {
        return tableEmptySeat;
      }
    }

    return emptySeat;
  }

  /**
   * 玩家叫庄家 .
   * 
   * @param player 玩家.
   * @param call . 是否叫庄
   */
  public ResMessage callDealer(Player player, boolean call) {
    SiRenNiuNiuSeat seat = dataMgr.getPlayerSeat(player.getId());
    if (seat == null) {
      LOG.error("[四人牛牛]玩家[{}][{}]不在牌桌中不能叫庄", player.getPlayerName(), player.getId());
      HuohuaAssert.isTrue(false, ErrorCode.INTERNAL_SERVER_ERROR);
    }

    if (seat.getCards().size() > 0) {
      LOG.error("[四人牛牛]玩家[{}][{}]正在游戏中不能叫庄", player.getPlayerName(), player.getId());
      HuohuaAssert.isTrue(false, ErrorCode.INTERNAL_SERVER_ERROR);
    }

    if (seat.getCallDealer() != null) {
      LOG.error("[四人牛牛]玩家[{}][{}]已经叫过庄不能不能重复叫庄", player.getPlayerName(), player.getId());
      HuohuaAssert.isTrue(false, ErrorCode.INTERNAL_SERVER_ERROR);
    }

    final SiRenNiuNiuTable table = seat.getTable();
    // 玩家叫过庄
    seat.setCallDealer(call);
    seat.stepFuture.cancel(false);

    LOG.info("[四人牛牛]玩家[{}][{}][{}]", seat.getPlayerName(), seat.getPlayerId(), call ? "叫庄" : "不叫");

    // 发送叫庄消息
    msgMgr.sendCallDealerMsg(seat, call ? 1 : 0);

    if (table.callDealerOver()) { // 叫庄结束
      gameExecutor.execute(() -> {
        List<SiRenNiuNiuSeat> callDealerSeats = table.callDealerSeats();

        long totalChips = 0;
        for (SiRenNiuNiuSeat callSeat : callDealerSeats) {
          totalChips += callSeat.getTotalChips();
        }

        long ranChips = RandCodeUtil.random(totalChips);
        long sum = 0;
        for (SiRenNiuNiuSeat callSeat : callDealerSeats) {
          sum += callSeat.getTotalChips();
          if (ranChips < sum) {
            callSeat.setDealer(true);
            break;
          }
        }

        msgMgr.sendCallDealerOverMsg(table);

        // 叫完庄之后下注（超时强退）
        int betTime = timeDic.map().get(3).getTime();
        for (SiRenNiuNiuSeat s : table.getSeats()) {
          if (s.getPlayerId() > 0) {
            if (!s.isDealer()) {
              // 强制退出
              delayExit(s, betTime + ADDED_DELAY, TimeUnit.SECONDS);
            }
          }
        }
      });
    }
    return ResMessage.DEFAULT;
  }

  /**
   * 玩家下注 .
   * 
   * @param player 玩家.
   * @param chips .
   */
  public ResMessage bet(Player player, long chips) {
    SiRenNiuNiuSeat seat = dataMgr.getPlayerSeat(player.getId());
    if (seat == null) {
      LOG.error("[四人牛牛]玩家[{}][{}]不在牌桌中不能下注", player.getPlayerName(), player.getId());
      HuohuaAssert.isTrue(false, ErrorCode.INTERNAL_SERVER_ERROR);
    }

    if (seat.getCards().size() > 0) {
      LOG.error("[四人牛牛]玩家[{}][{}]正在玩牌不能下注", player.getPlayerName(), player.getId());
      HuohuaAssert.isTrue(false, ErrorCode.INTERNAL_SERVER_ERROR);
    }

    if (seat.isDealer()) {
      LOG.error("[四人牛牛]玩家[{}][{}]是庄家不能下注", player.getPlayerName(), player.getId());
      HuohuaAssert.isTrue(false, ErrorCode.INTERNAL_SERVER_ERROR);
    }

    if (seat.getBetedchips() > 0) {
      LOG.error("[四人牛牛]玩家[{}][{}]已经下过注", player.getPlayerName(), player.getId());
      HuohuaAssert.isTrue(false, ErrorCode.INTERNAL_SERVER_ERROR);
    }
    SiRenNiuNiuTable table = seat.getTable();
    if (table.dealer() == null) {
      LOG.error("[四人牛牛]还未确定庄家,玩家[{}][{}]不能下注", player.getPlayerName(), player.getId());
      HuohuaAssert.isTrue(false, ErrorCode.INTERNAL_SERVER_ERROR);
    }

    List<Long> abledBetChips = abledBetChips(seat);
    if (!abledBetChips.contains(chips)) {
      LOG.error("[四人牛牛]玩家[{}][{}]下注[{}]不在列表{}中", player.getPlayerName(), player.getId(), chips,
          abledBetChips);
      HuohuaAssert.isTrue(false, ErrorCode.SIREN_NIUNIU_BET_CHIP_ILLEGAL);
    }

    /*
     * 下注 .
     */
    seat.setBetedchips(chips);
    // 取消定时退出
    seat.stepFuture.cancel(false);
    // 发送下注消息
    msgMgr.sendBetMsg(seat);

    if (table.betOver()) {
      LOG.info("[四人牛牛]闲家[{}][{}]下注[{}]结束,开始洗牌发牌", seat.getPlayerName(), seat.getPlayerId(), chips);
      doDealCards(table);
    }

    return ResMessage.DEFAULT;
  }

  private void doDealCards(SiRenNiuNiuTable table) {
    gameExecutor.execute(() -> {
      Collections.shuffle(table.getCards());
      // 控制牛牛发牌
      niuniuControl.controlDealCards(table);

      // 摊牌超时控制
      int showCardTime = timeDic.map().get(4).getTime();
      for (SiRenNiuNiuSeat s : table.getSeats()) {
        if (s.getPlayerId() > 0) {
          // 强制摊牌
          delayShowCard(s, showCardTime, TimeUnit.SECONDS);
        }
      }
    });
  }

  /**
   * 玩家摊牌 .
   * 
   * @param player 玩家.
   * @param playerCardIds . 玩家选择的3张牌的组合,如果玩家自己选择的牌是最优的牌则用玩家的
   */
  public ResMessage showdown(Player player, List<Integer> playerCardIds) {
    SiRenNiuNiuSeat seat = dataMgr.getPlayerSeat(player.getId());
    if (seat == null) {
      LOG.error("[四人牛牛]玩家[{}][{}]不在牌桌中不能摊牌", player.getPlayerName(), player.getId());
      HuohuaAssert.isTrue(false, ErrorCode.INTERNAL_SERVER_ERROR);
    }

    if (seat.isShowdown()) {
      LOG.error("[四人牛牛]玩家[{}][{}]重复摊牌", player.getPlayerName(), player.getId());
      HuohuaAssert.isTrue(false, ErrorCode.INTERNAL_SERVER_ERROR);
    }

    if (seat.getCards().size() == 0) {
      LOG.error("[四人牛牛]牌局还未开始，玩家[{}][{}]不能摊牌", player.getPlayerName(), player.getId());
      HuohuaAssert.isTrue(false, ErrorCode.INTERNAL_SERVER_ERROR);
    }

    /*
     * 计算玩家自己选择的牌是不是最优的牌 .
     */
    // 玩家选择的3张数字和
    int totalNum = 0;
    // 玩家自定义的最优牌
    List<NiuNiuCard> playerBestCards = new ArrayList<>();
    for (int i = 0; i < playerCardIds.size() && i < 3; i++) {
      NiuNiuCard card = NiuNiuCard.card(playerCardIds.get(i));
      playerBestCards.add(card);
      if (seat.getCards().contains(card)) {
        totalNum += card.num;
      } else {
        break;
      }

      // 有大王或者小王totalNum肯定是10的倍数
      if (i == 2 && (totalNum % 10 == 0 || playerBestCards.contains(NiuNiuCard.DA_WANG)
          || playerBestCards.contains(NiuNiuCard.XIAO_WANG))) {
        List<NiuNiuCard> seatCards = new ArrayList<>(seat.getCards());
        seatCards.removeAll(playerBestCards);
        // 玩家自定义牌组合的牛类型
        int niu = (seatCards.get(0).num + seatCards.get(1).num) % 10;
        NiuNiuCardsType playerBestCardsType =
            (niu == 0 ? NiuNiuCardsType.geCardsType(10) : NiuNiuCardsType.geCardsType(niu));
        if (seat.getCardsType() == playerBestCardsType) {
          seat.getBestCards().clear();
          seat.getBestCards().addAll(playerBestCards);
          seat.getBestCards().addAll(seatCards);
        }
      }
    }

    // 摊牌
    doShowdown(seat, false);

    return ResMessage.DEFAULT;
  }

  /**
   * 摊牌 .
   * 
   * @param seat 座位信息.
   * @param bill . 是否结算
   */
  private void doShowdown(SiRenNiuNiuSeat seat, boolean bill) {
    seat.setShowdown(true);
    seat.stepFuture.cancel(false);
    msgMgr.sendShowdownMsg(seat);

    SiRenNiuNiuTable table = seat.getTable();
    LOG.info("[四人牛牛]玩家[{}][{}]摊牌", seat.getPlayerName(), seat.getPlayerId());

    // 所有玩家都摊牌则游戏结束，并且结算
    if (table.showdown()) {
      gameOver(table);
    } else if (bill) {
      doBill(seat);
    }
  }

  /**
   * 该牌桌游戏结束 .
   * 
   * @param table .
   */
  private void gameOver(SiRenNiuNiuTable table) {
    // 结算信息
    List<BillInfo> billInfos = new ArrayList<>();
    for (SiRenNiuNiuSeat seat : table.getSeats()) {
      long playerId = seat.getPlayerId();
      if (playerId > 0) {
        BillInfo billInfo = new BillInfo();
        billInfo.setOrder(seat.getOrder());
        billInfo.setChips(seat.getBillChips());
        billInfos.add(billInfo);

        // 结算
        doBill(seat);

        // 强制退出
        int overTime = timeDic.map().get(1).getTime() + timeDic.map().get(5).getTime();
        delayExit(seat, overTime + ADDED_DELAY, TimeUnit.SECONDS);
      }
    }
    table.reset();

    msgMgr.sendGameOver(table, billInfos);

    if (checkClose(table)) {
      dataMgr.checkRemoveInstance(table.getRoom().getId().intValue(),
          table.getRoom().getInstanceId());
    }
  }

  /**
   * 座位结算 .
   * 
   * @param seat 座位信息.
   */
  private void doBill(SiRenNiuNiuSeat seat) {
    long playerId = seat.getPlayerId();
    SirenniuniuRoomDomain roomDomain = seat.getTable().getRoom().getRoomDomain();
    // 扣除输赢
    seat.setTotalChips(seat.getTotalChips() + seat.getBillChips());
    long billGold = convertGold(roomDomain, seat.getBillChips());

    // 如果满足条件发送牛牛公告
    sendedNotice(seat.getPlayerName(), seat.getCardsType().niu, seat.getBillChips());

    /*
     * 游戏结算金币 .
     */
    Player player = playerMgr.getPlayer(playerId);
    PlayerBo playerDom = playerMgr.selectPlayer(playerId);
    if (player != null) {
      playerMgr.addGold(player, billGold, LogReason.SIREN_NIUNIU_BILL);
    } else {
      playerMgr.addGold(playerDom, billGold, LogReason.SIREN_NIUNIU_BILL);
    }

    // 数据库输赢日志
    DbLogService.log(new Game13WinLoseLog(playerDom, seat, seat.getBillChips(), billGold,
        LogReason.SIREN_NIUNIU_BILL));

    seat.resetButTotalChips();

    // 发送筹码变化消息
    msgMgr.sendChipsChangeMsg(seat);

    // 触发房间大厅成员信息变更事件
    eventMgr.post(new RoomMemInfoUpdateEvent(playerMgr.getPlayer(playerId)));
  }

  /**
   * 如果满足条件发送牛牛公告 .
   * 
   * @param playerName .
   * @param cardsType . 牌型
   * @param chips . 赢得筹码
   */
  private void sendedNotice(String playerName, int cardsType, long chips) {

    for (GameNoticeDomainWrapper dom : gameNoticeDic.getGameDoms()
        .get(Game.SIREN_NIUNIU.getType())) {
      JSONObject conditionData = dom.conditionData();
      // 公告条件牌型
      Integer conditionCardsType = conditionData.getInteger("cardsType");
      // 公告条件赢得最小筹码
      Integer conditionMinChips = conditionData.getInteger("minChips");

      if (cardsType == conditionCardsType && chips >= conditionMinChips) {
        String sendContent = MessageFormat.format(dom.getContent(), playerName, chips);
        if (dom.getMarqueeShow() == BoolInteger.TRUE) { // 跑马灯公告
          noticeMsgMgr.sendMarqueeNoticeMsg(sendContent, NoticeType.GAME, dom.getInterval(),
              dom.getTimes(), dom.getColor());
        }

        if (dom.getChatShow() == BoolInteger.TRUE) { // 聊天公告
          chatMsgMgr.sendSysNoticeMsg(sendContent);
        }
      }
    }
  }

  /**
   * 该seat可用的下注的集合,用庄家和闲家中最小的一家来计算,闲家对应的庄家比例筹码=（对应闲家筹码/所有闲家筹码）*庄家筹码， . 闲家对应的庄家比例筹码和闲家筹码比较
   * 
   * @param seats .
   * @return
   */
  private List<Long> abledBetChips(SiRenNiuNiuSeat seat) {
    SiRenNiuNiuTable table = seat.getTable();
    SiRenNiuNiuRoom room = table.getRoom();
    SirenniuniuRoomDomain roomDomain = room.getRoomDomain();

    // 闲家合计筹码
    long playesChips = table.playesChips();
    SiRenNiuNiuSeat dealer = table.dealer();
    long ratioChips = dealer.getTotalChips() * seat.getTotalChips() / playesChips;

    long minChips = ratioChips > seat.getTotalChips() ? seat.getTotalChips() : ratioChips;

    long baseBet = roomDomain.getBaseBet();
    List<Integer> betOptions = roomDomain.getBetOptions();

    List<Long> result = new ArrayList<Long>();
    for (int i = betOptions.size() - 1; i >= 0; i--) {
      Integer multiple = betOptions.get(i);
      Long maxBetChips = baseBet * multiple;
      if (maxBetChips <= minChips) {
        for (int j = 0; j <= i; j++) {
          result.add(baseBet * betOptions.get(j));
        }
        break;
      }
    }
    return result;
  }

  /**
   * 清除玩家,服务器出现bug，玩家卡在游戏中，后台可以清除玩家 .
   * 
   * @param playerId 玩家id.
   */
  private void doClearGameData(long playerId) {
    try {

      dataMgr.removePlayerSeat(playerId);
      /*
       * 清除玩家房间数据 .
       */
      for (SiRenNiuNiuRoom room : dataMgr.allRooms()) {
        room.getPlayers().remove(playerId);

        /*
         * 清除房间中玩家所在桌子数据 .
         */
        for (SiRenNiuNiuTable table : room.getTables().values()) {

          // 是否在桌子中
          boolean inTable = false;
          for (SiRenNiuNiuSeat s : table.getSeats()) {
            if (s.getPlayerId() > 0 && s.getPlayerId() == playerId) {
              inTable = true;
              break;
            }
          }

          if (inTable) { // 在桌子中清除数据
            for (SiRenNiuNiuSeat s : table.getSeats()) {
              if (s.getPlayerId() > 0) {
                dataMgr.removePlayerSeat(s.getPlayerId());
                room.getPlayers().remove(s.getPlayerId());

                /*
                 * 清除座位数据 .
                 */
                s.clear();
                Player player = playerMgr.getPlayer(s.getPlayerId());
                if (player != null) {
                  loginMgr.noticeLogoutForGameExit(player);
                }
              }
            }
          }
        }
      }

      Player player = playerMgr.getPlayer(playerId);
      if (player != null) {
        loginMgr.noticeLogoutForGameExit(player);
      }
    } catch (Exception e) {
      LOG.error("[四人牛牛]清除玩家[" + playerId + "]卡限失败", e);
    }
  }
}
